package com.example.instagramgallery;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
/**
* This class implements zooming and panning using touch gestures
* @author Colin
*
*/
public class ZoomImageView extends ImageView {
// used to track pinching
private ScaleGestureDetector detector;
private float scale = 1f;
private static float minZoom = 0.5f, maxZoom = 3f;
private Matrix transform;
// used to track dragging a finger
private int activePointerID = -1;
private float lastX, lastY;
private int offsetX = 0, offsetY = 0;
// stores the size of the screen along with its smallest dimension
private int width, height;
private int imageWidth, imageHeight;
public ZoomImageView(Context context, AttributeSet set, int defStyle) {
super(context, set, defStyle);
}
public ZoomImageView(Context context, AttributeSet set) {
super(context, set);
}
public ZoomImageView(Context context) {
super(context);
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
imageWidth = bm.getWidth();
imageHeight = bm.getHeight();
setupPinchZoom();
Log.i("crb", "image size = (" + imageWidth + ", " + imageHeight + ")");
}
public void setupPinchZoom() {
// get the size of the screen
DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
width = metrics.widthPixels;
height = metrics.heightPixels;
// set the size of the picture to fit the smallest orientation of the screen
if (width > height) {
scale = (float) height / imageHeight;
} else {
scale = (float) width / imageWidth;
}
// adjust scaling limits if needed
if (scale > maxZoom)
maxZoom = scale;
if (scale < minZoom)
minZoom = scale;
// create a transformation matrix and set the scale and translation based on the initial scale
transform = new Matrix();
transform.setScale(scale, scale);
transform.postTranslate(-306 * scale, -306 * scale);
// set up a listener for pinch and zoom
detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// exit event without handling if image is not yet loaded
//if (this.getDrawable() == null)
//return true;
if (detector != null)
detector.onTouchEvent(event);
// get the type of action associated with the event and switch on it
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = MotionEventCompat.getActionIndex(event);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// save our last position and the pointer id
lastX = x;
lastY = y;
activePointerID = MotionEventCompat.getPointerId(event, pointerIndex);
break; }
case MotionEvent.ACTION_MOVE: {
// fetch the active pointer index
final int pointerIndex = MotionEventCompat.findPointerIndex(event, activePointerID);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// only move if not scaling?
if (detector != null && !detector.isInProgress()) {
final float dx = x - lastX;
final float dy = y - lastY;
// adjust canvas offset
offsetX += dx;
offsetY += dy;
// remember these new coordinates
lastX = x;
lastY = y;
invalidate();
}
break; }
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
activePointerID = -1;
break; }
case MotionEvent.ACTION_POINTER_UP: {
// get pointer information
final int pointerIndex = MotionEventCompat.getActionIndex(event);
final int pointerID = MotionEventCompat.getPointerId(event, pointerIndex);
// if the pointer we were tracking was lifted, choose a new pointer
if (pointerID == activePointerID) {
final int newPointerIndex = (pointerIndex == 0 ? 1 : 0);
lastX = MotionEventCompat.getX(event, newPointerIndex);
lastY = MotionEventCompat.getY(event, newPointerIndex);
activePointerID = MotionEventCompat.getPointerId(event, newPointerIndex);
}
break; }
}
fitToScreen();
return true;
}
public void fitToScreen() {
//Log.i("crb", "limit = " + ((scale * imageWidth - width) / 2));
//Log.i("crb", "left edge at = " + (offsetX));
int edgeX;
if (scale * imageWidth >= width)
edgeX = (int) ((scale * imageWidth - width) / 2);
else
edgeX = (int) ((width - scale * imageWidth) / 2);
if (offsetX > edgeX)
offsetX = edgeX;
else if (offsetX < -edgeX)
offsetX = -edgeX;
int edgeY;
if (scale * imageHeight <= height)
edgeY = (int) ((scale * imageHeight - height) / 2);
else
edgeY = (int) ((height - scale * imageHeight) / 2);
if (offsetY < edgeY)
offsetY = edgeY;
else if (offsetY > -edgeY)
offsetY = -edgeY;
}
@Override
public void onDraw(Canvas canvas) {
BitmapDrawable draw = (BitmapDrawable) this.getDrawable();
if (draw != null) {
// draw a bitmap with the desired scaling and translate the canvas to pan
canvas.translate(width / 2 + offsetX, height / 2 + offsetY);
canvas.drawBitmap(draw.getBitmap(), transform, null);
}
}
/**
* An extension of the android scaled gesture listener that implements adjusting the
* image scale based on how the user pinches the screen
* @author Colin
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
// adjust our scaling based on the gesture
scale *= detector.getScaleFactor();
scale = Math.max(minZoom, Math.min(scale, maxZoom));
// adjust matrix
transform.setScale(scale, scale);
transform.postTranslate(-306 * scale, -306 * scale);
// tell the canvas to redraw
invalidate();
return true;
}
}
}